﻿using System;
using System.Net.Sockets;
using System.Text;

namespace Gonte.Networking
{
    /// <summary>
    /// Reads data from a network stream
    /// </summary>
    public class NetworkStreamReaderWriter
    {
        #region Constructors

        public NetworkStreamReaderWriter(INetworkConnectionHandler handler, NetworkStream stream, int size, ClientData clientData)
        {
            ExceptionThrower.ThrowWhenNull("handler", handler);
            ExceptionThrower.ThrowWhenNull("stream", stream);
            ExceptionThrower.ThrowWhenNot(size > 0, "The buffer size must be greater than zero");

            _handler = handler;
            _stream = stream;
            _buffer = new byte[size];
            _clientData = clientData;
        }

        #endregion

        #region Methods

        public void ReadAsynchronous()
        {
            _asyncResult = _stream.BeginRead(_buffer, 0, _buffer.Length, EndRead, null);
        }

        public void Write(string message)
        {
            try
            {
                byte[] bytesToSend = System.Text.Encoding.ASCII.GetBytes(message);
                _stream.Write(bytesToSend, 0, bytesToSend.Length);
                _stream.Flush();
            }
            catch (Exception exception)
            {
                _handler.HandleException(this, exception, false);
            }
        }

        #endregion

        #region Properties

        public ClientData ClientData
        {
            get { return _clientData; }
            // set { _clientData = value; }
        }

        public IAsyncResult AsyncResult
        {
            get { return _asyncResult; }
            //set { _asyncResult = value; }
        } 

        #endregion

        #region Helpers

        private void EndRead(IAsyncResult asyncResult)
        {
            int bytesRead;

            try
            {
                lock (_stream)
                {
                    bytesRead = _stream.EndRead(asyncResult);
                }

                if (bytesRead < 1) // Client disconnected
                {
                    _handler.HandleClientDisconnected(this);
                    return;
                }
                
                // Handle the request
                string request = Encoding.ASCII.GetString(_buffer, 0, bytesRead);
                _handler.HandleRequest(this, request, _stream.DataAvailable);

                // Continue reading from the client
                if (_stream.DataAvailable)
                {
                    lock (_stream) 
                    {
                        _stream.BeginRead(_buffer, 0, _buffer.Length, EndRead, null);
                    }
                }
            }
            catch (Exception exception)
            {
                _handler.HandleException(this, exception, true);
            }
        } 

        #endregion

        #region Fields

        private INetworkConnectionHandler _handler;
		private NetworkStream _stream;
        private byte[] _buffer;
        private ClientData _clientData; // User specific data passed to the reader 
        private IAsyncResult _asyncResult;

	    #endregion
    }
}
